package formework.context;

import formework.annotation.DemoAutowired;
import formework.annotation.DemoController;
import formework.annotation.DemoService;
import formework.aop.DemoAopProxy;
import formework.aop.DemoCglibAopProxy;
import formework.aop.DemoJdkDynamicAopProxy;
import formework.aop.support.DemoAdvisedSupport;
import formework.beans.DemoBeanWrapper;
import formework.beans.config.DemoBeanDefinition;
import formework.beans.support.DemoBeanDefinitionReader;
import formework.beans.support.DemoDefaultListableBeanFactory;
import formework.context.support.DemoAbstractApplicationContext;
import formework.webmvc.DemoBeanFactory;
import sun.net.ftp.FtpClient;

import javax.swing.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DemoApplicationContext extends DemoDefaultListableBeanFactory implements DemoBeanFactory {

    private String [] configLoactions;
    private DemoBeanDefinitionReader reader;
    //Bean的缓存池
    private Map<String,Object> singletonBeanCacheMap = new HashMap<String,Object>();
    //beanWrapper缓存池
    private Map<String,DemoBeanWrapper>  factoryBeanInstanceCache = new ConcurrentHashMap<String,DemoBeanWrapper>();

    public DemoApplicationContext(String... configLoactions){
        this.configLoactions = configLoactions;
        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    @Override
    public Object getBean(String beanName) {
        DemoBeanDefinition demoBeanDefinition = beanDefinitionMap.get(beanName);

        Object o = instantiateBean(demoBeanDefinition);

        //这个逻辑还不严谨，自己可以去参考Spring源码
        //工厂模式 + 策略模式
        //GPBeanPostProcessor postProcessor = new GPBeanPostProcessor();
       // postProcessor.postProcessBeforeInitialization(instance,beanName)
        DemoBeanWrapper demoBeanWrapper = new DemoBeanWrapper(o);

        this.factoryBeanInstanceCache.put(beanName,demoBeanWrapper);
        //postProcessor.postProcessAfterInitialization(instance,beanName);

        populateBean(beanName,demoBeanDefinition, demoBeanWrapper);


        return factoryBeanInstanceCache.get(beanName).getWrappedInstance();
    }



    private void populateBean(String beanName, DemoBeanDefinition beanDefinition, DemoBeanWrapper beanWrapper) {

        Object wrappedInstance = beanWrapper.getWrappedInstance();

        Class<?> wrapperedClass = beanWrapper.getWrapperedClass();

        if(!(wrapperedClass.isAnnotationPresent(DemoController.class) || !(wrapperedClass.isAnnotationPresent(DemoService.class)))){
            return;
        }
        Field[] fields = wrapperedClass.getDeclaredFields();
        for (Field field : fields) {

            DemoAutowired annotation = field.getAnnotation(DemoAutowired.class);

            String autowiredBeanName = annotation.value().trim();

            if("".equals(autowiredBeanName)){
                autowiredBeanName = field.getType().getName();
            }

            field.setAccessible(true);
            try {
                if(factoryBeanInstanceCache.get(autowiredBeanName) == null ){
                    continue;
                }

                field.set(wrappedInstance,factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }


    }

    private Object instantiateBean( DemoBeanDefinition demoBeanDefinition) {

        Object o = null;
        String beanClassName = demoBeanDefinition.getBeanClassName();
        try {
            if(singletonBeanCacheMap.containsKey(beanClassName)){
                o=singletonBeanCacheMap.get(beanClassName);

            }else{
                Class clazz = Class.forName(beanClassName);

                o=clazz.newInstance();

               DemoAdvisedSupport config = instantionAopConfig(demoBeanDefinition);
                config.setTargetClass(clazz);
                config.setTarget(o);

                if(config.pointCutMatch()){
                    o = createProxy(config).getProxy();
                }

                
                if(demoBeanDefinition.isSington()){
                    singletonBeanCacheMap.put(demoBeanDefinition.getBeanClassName(),o);
                    singletonBeanCacheMap.put(demoBeanDefinition.getFactoryBeanName(),o);
                }
                return o;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return  null;
    }

    private DemoAopProxy createProxy(DemoAdvisedSupport config) {

        Class<?> targetClass = config.getTargetClass();

        if(targetClass.getInterfaces().length>0){
            return  new DemoJdkDynamicAopProxy(config);
        }

        return new DemoCglibAopProxy(config);
    }

    private DemoAdvisedSupport instantionAopConfig(DemoBeanDefinition demoBeanDefinition) {

        return null;
    }

    @Override
    public void refresh() throws Exception {

        //1、定位

        reader = new DemoBeanDefinitionReader(configLoactions);

        //2、加载
        List<DemoBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();

        //3、注册
        doRegisterBeanDefinition(beanDefinitions);
        //4、把不是延时加载的类，有提前初始化
        doAutowrited();


    }
    private void  doAutowrited(){
        for (Map.Entry<String, DemoBeanDefinition> beanEntry : beanDefinitionMap.entrySet()){
            String key = beanEntry.getKey();
            if(!beanEntry.getValue().isLazyInit()){
                getBean(key);
            }

        }
    }
    private void doRegisterBeanDefinition(List<DemoBeanDefinition> beanDefinitions) throws Exception {

        for (DemoBeanDefinition beanDefinition: beanDefinitions) {
            if(super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
                throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
        }
        //到这里为止，容器初始化完毕
    }
}
